package com.itcast.springboot3x.aop;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author zhangtian105
 * @date 2023/5/25
 * @apiNote
 */
@Slf4j
@Aspect
@Component
public class ServerResponseAop {
    private final Cache<String, AtomicLong> cache ;

    {
        cache = Caffeine.newBuilder()
                .expireAfterWrite(60,TimeUnit.DAYS)
                //.refreshAfterWrite(35, TimeUnit.MINUTES)
                .build() ;
    }

    @Pointcut(value = "execution(* com.itcast.springboot3x.controller.*.*(..))")
    private void pointcut() {}

    @Around("pointcut()")
    public Object aroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        Object process = joinPoint.proceed() ;

        ReflectionUtils.doWithFields(process.getClass(), field -> {
            ReflectionUtils.makeAccessible(field);
            Object obj = field.get(process) ;
            if (Objects.isNull(obj)) {
                String cackeKey = process.getClass().getSimpleName() + ":" + field.getName() ;
                AtomicLong atomicLong = cache.get(cackeKey, t -> new AtomicLong(0L)) ;
                atomicLong.incrementAndGet() ;
                cache.put(cackeKey, atomicLong);
                /*ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field, process, null);*/
            }
        }, field -> {
            // 获取属性上注解
            JsonInclude jsonInclude = field.getAnnotation(JsonInclude.class);
            return jsonInclude != null;
        });

        return process ;
    }

    @AfterReturning(value = "pointcut()",returning = "result")
    public Object afterAnyPublicMethod(JoinPoint joinPoint, Object result) {
        log.info("属性计数值: {}", cache.asMap()) ;
        return result ;
    }
}
